#include "mysql_pool.h"

//线程安全的懒汉单例模式
Connection_pool *Connection_pool::getConnectionPool()
{
    static Connection_pool pool; //静态局部变量由编译器进行自动的lock unlock
    return &pool;
}

void Connection_pool::produceConnectionTask()
{

    for (;;)
    {
        unique_lock<std::mutex> lck(_queueMutex);
        while (!_connectionQue.empty())
        {
            cv.wait(lck); //队列不为空，线程进入等待状态
        }
        //连接数量没有达到上限，继续创建新的连接

        if (_connectionCnt >= _maxSize)
        {
            Connection *p = new Connection();
            //这些选项需要从配置文件中加载
            p->connect(_ip, _port, _username, _password, _dbname);
            _connectionQue.push(p);
            _connectionCnt++; //连接数量记录
        }
        //通知所有消费者线程可以连接了
        cv.notify_all();
    }
}

void Connection_pool::scannerConnectionTask()
{

    //定时
    for (;;)
    {
        //通过sleep模拟定时效果
        std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime));

        //扫描整个队列，释放多余连接
        unique_lock<std::mutex> lock(_queueMutex);

        //扫描整个队列，释放多余队列
        while (_connectionCnt > _initSize)
        {
            Connection *p = _connectionQue.front();
            if (p->getAliveTime() >= (_maxIdleTime * 1000))
            {
                _connectionQue.pop();
                _connectionCnt--;
                delete p; //释放连接
            }
            else
            {
                break; //队头连接没有超过mnax,其他肯定没有
            }
        }
    }
}

Connection_pool::Connection_pool()
{
    if (!loadConfigFile())
    {
        std::cout << "load failed\n"
                  << std::endl;
        return;
    }

    //创建初始连接数量
    for (int i = 0; i < _initSize; ++i)
    {

        Connection *p = new Connection();
        //这些选项需要从配置文件中加载
        p->connect(_ip, _port, _username, _password, _dbname);
        //刷新开始空闲的起始时间
        p->refreshAliveTime();

        _connectionQue.push(p);
        _connectionCnt++; //连接数量记录
    }
    //启动一个新线程，作为连接的生产者
    // this表示绑定当前对象
    std::thread produce(std::bind(&Connection_pool::produceConnectionTask, this));

    produce.detach();

    //启动一个新的线程，扫描超过maxIdleTime时间的空闲连接，进行对应的连接回收
    std::thread scanner(std::bind(&Connection_pool::scannerConnectionTask, this));

    scanner.detach();
}

//加载配置项
bool Connection_pool::loadConfigFile()
{

    FILE *fp = fopen("/home/ubuntu/project2/mysql_pool/mysql.ini", "r");
    if (fp == nullptr)
    {
        std::cout << "mysql.ini not exist" << std::endl;
        return false;
    }

    //文件没有到末尾，继续寻找
    while (!feof(fp))
    {
        char line[1024] = {0};
        fgets(line, 1024, fp); //从文件指针中读取一行数据
        string str = line;
        int idx = str.find('=', 0);
        if (idx == -1)
        {
            continue;
        }

        int endix = str.find('\n', idx); //寻找回车

        string key = str.substr(0, idx);
        string value = str.substr(idx + 1, endix - idx - 1);
        std::cout << key << ":" << value << std::endl;
        if (key == "ip")
        {
            _ip = value;
        }
        else if (key == "port")
        {

            _port = atoi(value.c_str());
        }
        else if (key == "username")
        {
            _username = value.c_str();
        }
        else if (key == "password")
        {
            _password = value;
        }
        else if (key == "initSize")
        {

            _initSize = atoi(value.c_str());
        }
        else if (key == "maxSize")
        {
            _maxSize = atoi(value.c_str());
        }
        else if (key == "maxIdleTime")
        {
            _maxIdleTime = atoi(value.c_str());
        }
        else if (key == "connectionTimeout")
        {

            _connectionTimeout = atoi(value.c_str());
        }
        else if (key == "dbname")
        {

            _dbname = value.c_str();
        }

        cout << endl;
    }
    return true;
}

//获取空闲连接
shared_ptr<Connection> Connection_pool::getConnection()
{

    std::unique_lock<std::mutex> lock(_queueMutex);

    while (_connectionQue.empty())
    {
        //不要写sleep
        // cv.wait_for(lock, std::chrono::milliseconds(_connectionTimeout));

        if (cv_status::timeout == cv.wait_for(lock, std::chrono::milliseconds(_connectionTimeout)))
        {
            //等待这么长时间依旧为空
            if (_connectionQue.empty())
            {
                std::cout << "获取空闲连接失败。。。" << std::endl;
                return nullptr;
            }
        }
    }

    // shared_ptr析构时会把资源直接析构掉，因此需要进行自定义删除函数，把Connection直接归还到—_queue中

    shared_ptr<Connection> sp(_connectionQue.front(), [&](Connection *pcon)
                              {
                                  //这里是在服务器应用线程中实现的，所以一定要考虑线程安全
                                  unique_lock<std::mutex> lock(_queueMutex);
                                  pcon->refreshAliveTime();   //刷新空闲时间
                                  _connectionQue.push(pcon); });
    _connectionQue.pop();
    cv.notify_all(); //消费连接完之后
    return sp;
}